home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 41 / Amiga Format CD41 (1999-06)(Future Publishing)(GB)[!][issue 1999-07].iso / -seriously_amiga- / programming / other / scm / slib / glob.scm < prev    next >
Text File  |  1999-04-19  |  7KB  |  217 lines

  1. ;;; "glob.scm" String matching for filenames (a la BASH).
  2. ;;; Copyright (C) 1998 Radey Shouman.
  3. ;
  4. ;Permission to copy this software, to redistribute it, and to use it
  5. ;for any purpose is granted, subject to the following restrictions and
  6. ;understandings.
  7. ;
  8. ;1.  Any copy made of this software must include this copyright notice
  9. ;in full.
  10. ;
  11. ;2.  I have made no warrantee or representation that the operation of
  12. ;this software will be error-free, and I am under no obligation to
  13. ;provide any services, by way of maintenance, update, or otherwise.
  14. ;
  15. ;3.  In conjunction with products arising from the use of this
  16. ;material, there shall be no use of my name in any advertising,
  17. ;promotional, or sales literature without prior written consent in
  18. ;each case.
  19.  
  20. ;;$Header: /usr/local/cvsroot/slib/glob.scm,v 1.11 1998/12/16 20:32:15 radey Exp $
  21. ;;$Name:  $
  22.  
  23. (define (glob:pattern->tokens pat)
  24.   (cond
  25.    ((string? pat)
  26.     (let loop ((i 0)
  27.            (toks '()))
  28.       (if (>= i (string-length pat))
  29.       (reverse toks)
  30.       (let ((pch (string-ref pat i)))
  31.         (case pch
  32.           ((#\? #\*)
  33.            (loop (+ i 1)
  34.              (cons (substring pat i (+ i 1)) toks)))
  35.           ((#\[)
  36.            (let ((j
  37.               (let search ((j (+ i 2)))
  38.             (cond
  39.              ((>= j (string-length pat))
  40.               (slib:error 'glob:make-matcher 
  41.                       "unmatched [" pat))
  42.              ((char=? #\] (string-ref pat j))
  43.               (if (and (< (+ j 1) (string-length pat))
  44.                    (char=? #\] (string-ref pat (+ j 1))))
  45.                   (+ j 1)
  46.                   j))
  47.              (else (search (+ j 1)))))))
  48.          (loop (+ j 1) (cons (substring pat i (+ j 1)) toks))))
  49.           (else 
  50.            (let search ((j (+ i 1)))
  51.          (cond ((= j (string-length pat))
  52.             (loop j (cons (substring pat i j) toks)))
  53.                ((memv (string-ref pat j) '(#\? #\* #\[))
  54.             (loop j (cons (substring pat i j) toks)))
  55.                (else (search (+ j 1)))))))))))
  56.    ((pair? pat)
  57.     (for-each (lambda (elt) (or (string? elt)
  58.                 (slib:error 'glob:pattern->tokens
  59.                         "bad pattern" pat)))
  60.           pat)
  61.     pat)
  62.    (else (slib:error 'glob:pattern->tokens "bad pattern" pat))))
  63.  
  64. (define (glob:make-matcher pat ch=? ch<=?)
  65.   (define (match-end str k kmatch)
  66.     (and (= k (string-length str)) (reverse (cons k kmatch))))
  67.   (define (match-str pstr nxt)
  68.     (let ((plen (string-length pstr)))
  69.       (lambda (str k kmatch)
  70.     (and (<= (+ k plen) (string-length str))
  71.          (let loop ((i 0))
  72.            (cond ((= i plen)
  73.               (nxt str (+ k plen) (cons k kmatch)))
  74.              ((ch=? (string-ref pstr i)
  75.                 (string-ref str (+ k i)))
  76.               (loop (+ i 1)))
  77.              (else #f)))))))
  78.   (define (match-? nxt)
  79.     (lambda (str k kmatch)
  80.       (and (< k (string-length str))
  81.        (nxt str (+ k 1) (cons k kmatch)))))
  82.   (define (match-set1 chrs)
  83.     (let recur ((i 0))
  84.       (cond ((= i (string-length chrs))
  85.          (lambda (ch) #f))
  86.         ((and (< (+ i 2) (string-length chrs))
  87.           (char=? #\- (string-ref chrs (+ i 1))))
  88.          (let ((nxt (recur (+ i 3))))
  89.            (lambda (ch)
  90.          (or (and (ch<=? ch (string-ref chrs (+ i 2)))
  91.               (ch<=? (string-ref chrs i) ch))
  92.              (nxt ch)))))
  93.         (else
  94.          (let ((nxt (recur (+ i 1)))
  95.            (chrsi (string-ref chrs i)))
  96.            (lambda (ch)
  97.           (or (ch=? chrsi ch) (nxt ch))))))))
  98.   (define (match-set tok nxt)
  99.     (let ((chrs (substring tok 1 (- (string-length tok) 1))))
  100.       (if (and (positive? (string-length chrs))
  101.            (memv (string-ref chrs 0) '(#\^ #\!)))
  102.       (let ((pred (match-set1 (substring chrs 1 (string-length chrs)))))
  103.         (lambda (str k kmatch)
  104.           (and (< k (string-length str))
  105.            (not (pred (string-ref str k)))
  106.            (nxt str (+ k 1) (cons k kmatch)))))
  107.       (let ((pred (match-set1 chrs)))
  108.         (lambda (str k kmatch)
  109.           (and (< k (string-length str))
  110.            (pred (string-ref str k))
  111.            (nxt str (+ k 1) (cons k kmatch))))))))
  112.   (define (match-* nxt)
  113.     (lambda (str k kmatch)
  114.       (let ((kmatch (cons k kmatch)))
  115.     (let loop ((kk (string-length str)))
  116.       (and (>= kk k)
  117.            (or (nxt str kk kmatch)
  118.            (loop (- kk 1))))))))
  119.  
  120.   (let ((matcher
  121.      (let recur ((toks (glob:pattern->tokens pat)))
  122.        (if (null? toks)
  123.            match-end
  124.            (let ((pch (or (string=? (car toks) "")
  125.                   (string-ref (car toks) 0))))
  126.          (case pch
  127.            ((#\?) (match-? (recur (cdr toks))))
  128.            ((#\*) (match-* (recur (cdr toks))))
  129.            ((#\[) (match-set (car toks) (recur (cdr toks))))
  130.            (else (match-str (car toks) (recur (cdr toks))))))))))
  131.     (lambda (str) (matcher str 0 '()))))
  132.  
  133. (define (glob:caller-with-matches pat proc)
  134.   (define (glob:wildcard? pat)
  135.     (cond ((string=? pat "") #f)
  136.       ((memv (string-ref pat 0) '(#\* #\? #\[)) #t)
  137.       (else #f)))
  138.   (let* ((toks (glob:pattern->tokens pat))
  139.      (wild? (map glob:wildcard? toks))
  140.      (matcher (glob:make-matcher toks char=? char<=?)))
  141.     (lambda (str)
  142.       (let loop ((inds (matcher str))
  143.          (wild? wild?)
  144.          (res '()))
  145.     (cond ((not inds) #f)
  146.           ((null? wild?)
  147.            (apply proc (reverse res)))
  148.           ((car wild?)
  149.            (loop (cdr inds)
  150.              (cdr wild?)
  151.              (cons (substring str (car inds) (cadr inds)) res)))
  152.           (else
  153.            (loop (cdr inds) (cdr wild?) res)))))))
  154.  
  155. (define (glob:make-substituter pattern template ch=? ch<=?)
  156.   (define (wildcard? pat) 
  157.     (cond ((string=? pat "") #f)
  158.       ((memv (string-ref pat 0) '(#\* #\? #\[)) #t)
  159.       (else #f)))
  160.   (define (countq val lst)
  161.     (do ((lst lst (cdr lst))
  162.      (c 0 (if (eq? val (car lst)) (+ c 1) c)))
  163.     ((null? lst) c)))
  164.   (let ((tmpl-literals (map (lambda (tok)
  165.                   (if (wildcard? tok) #f tok))
  166.                 (glob:pattern->tokens template)))
  167.     (pat-wild? (map wildcard? (glob:pattern->tokens pattern)))
  168.     (matcher (glob:make-matcher pattern ch=? ch<=?)))
  169.     (or (= (countq #t pat-wild?) (countq #f tmpl-literals))
  170.     (slib:error 'glob:make-substituter
  171.             "number of wildcards doesn't match" pattern template))
  172.     (lambda (str)
  173.       (let ((indices (matcher str)))
  174.     (and indices
  175.          (let loop ((inds indices)
  176.             (wild? pat-wild?)
  177.             (lits tmpl-literals)
  178.             (res '()))
  179.            (cond 
  180.         ((null? lits)
  181.          (apply string-append (reverse res)))
  182.         ((car lits)
  183.          (loop inds wild? (cdr lits) (cons (car lits) res)))
  184.         ((null? wild?)        ;this should never happen.
  185.          (loop '() '() lits res))
  186.         ((car wild?)
  187.          (loop (cdr inds) (cdr wild?) (cdr lits)
  188.                (cons (substring str (car inds) (cadr inds))
  189.                  res)))
  190.         (else
  191.          (loop (cdr inds) (cdr wild?) lits res)))))))))
  192.  
  193.  
  194. (define (glob:match?? pat)
  195.   (glob:make-matcher pat char=? char<=?))
  196. (define (glob:match-ci?? pat)
  197.   (glob:make-matcher pat char-ci=? char-ci<=?))
  198. (define filename:match?? glob:match??)
  199. (define filename:match-ci?? glob:match-ci??)
  200.  
  201. (define (glob:substitute?? pat templ)
  202.   (glob:make-substituter pat templ char=? char<=?))
  203. (define (glob:substitute-ci?? pat templ)
  204.   (glob:make-substituter pat templ char-ci=? char-ci<=?))
  205. (define filename:substitute?? glob:substitute??)
  206. (define filename:substitute-ci?? glob:substitute-ci??)
  207.  
  208. (define (replace-suffix str old new)
  209.   (let* ((f (glob:substitute?? (list "*" old) (list "*" new)))
  210.      (g (lambda (st)
  211.           (or (f st)
  212.           (slib:error 'replace-suffix "suffix doesn't match:"
  213.                   old st)))))
  214.     (if (pair? str)
  215.     (map g str)
  216.     (g str))))
  217.